#ifndef REGISTERSVIEWX86_H
#define REGISTERSVIEWX86_H

#include <QMap>
#include <QScrollArea>
#include <QSet>

#include "Bridge.h"

class CPUWidget;
class CPUMultiDump;
class QPushButton;

namespace Ui {
class RegistersView;
}

#undef _WIN64

class RegistersViewX86 : public QScrollArea {
  Q_OBJECT

 public:
  // all possible register ids
  enum REGISTER_NAME : int {
    CAX,
    CCX,
    CDX,
    CBX,
    CDI,
    CBP,
    CSI,
    CSP,
#ifdef _WIN64
    R8,
    R9,
    R10,
    R11,
    R12,
    R13,
    R14,
    R15,
#endif  //_WIN64
    CIP,
    EFLAGS,
    CF,
    PF,
    AF,
    ZF,
    SF,
    TF,
    IF,
    DF,
    OF,
    GS,
    FS,
    ES,
    DS,
    CS,
    SS,
    LastError,
    LastStatus,
    DR0,
    DR1,
    DR2,
    DR3,
    DR6,
    DR7,
    // x87 stuff
    x87r0,
    x87r1,
    x87r2,
    x87r3,
    x87r4,
    x87r5,
    x87r6,
    x87r7,
    x87st0,
    x87st1,
    x87st2,
    x87st3,
    x87st4,
    x87st5,
    x87st6,
    x87st7,
    x87TagWord,
    x87ControlWord,
    x87StatusWord,
    // x87 Tag Word fields
    x87TW_0,
    x87TW_1,
    x87TW_2,
    x87TW_3,
    x87TW_4,
    x87TW_5,
    x87TW_6,
    x87TW_7,
    // x87 Status Word fields
    x87SW_B,
    x87SW_C3,
    x87SW_TOP,
    x87SW_C2,
    x87SW_C1,
    x87SW_O,
    x87SW_ES,
    x87SW_SF,
    x87SW_P,
    x87SW_U,
    x87SW_Z,
    x87SW_D,
    x87SW_I,
    x87SW_C0,
    // x87 Control Word fields
    x87CW_IC,
    x87CW_RC,
    x87CW_PC,
    x87CW_PM,
    x87CW_UM,
    x87CW_OM,
    x87CW_ZM,
    x87CW_DM,
    x87CW_IM,
    // MxCsr
    MxCsr,
    MxCsr_FZ,
    MxCsr_PM,
    MxCsr_UM,
    MxCsr_OM,
    MxCsr_ZM,
    MxCsr_IM,
    MxCsr_DM,
    MxCsr_DAZ,
    MxCsr_PE,
    MxCsr_UE,
    MxCsr_OE,
    MxCsr_ZE,
    MxCsr_DE,
    MxCsr_IE,
    MxCsr_RC,
    // MMX and XMM
    MM0,
    MM1,
    MM2,
    MM3,
    MM4,
    MM5,
    MM6,
    MM7,
    XMM0,
    XMM1,
    XMM2,
    XMM3,
    XMM4,
    XMM5,
    XMM6,
    XMM7,
#ifdef _WIN64
    XMM8,
    XMM9,
    XMM10,
    XMM11,
    XMM12,
    XMM13,
    XMM14,
    XMM15,
#endif  //_WIN64
        // YMM
    YMM0,
    YMM1,
    YMM2,
    YMM3,
    YMM4,
    YMM5,
    YMM6,
    YMM7,
#ifdef _WIN64
    YMM8,
    YMM9,
    YMM10,
    YMM11,
    YMM12,
    YMM13,
    YMM14,
    YMM15,
#endif  //_WIN64
    UNKNOWN
  };

  // contains viewport position of register
  struct Register_Position {
    int line;
    int start;
    int valuesize;
    int labelwidth;

    Register_Position(int l, int s, int w, int v) {
      line = l;
      start = s;
      valuesize = v;
      labelwidth = w;
    }
    Register_Position() {
      line = 0;
      start = 0;
      valuesize = 0;
      labelwidth = 0;
    }
  };

  // tracks position of a register relative to other registers
  struct Register_Relative_Position {
    REGISTER_NAME left;
    REGISTER_NAME right;
    REGISTER_NAME up;
    REGISTER_NAME down;

    Register_Relative_Position(REGISTER_NAME l, REGISTER_NAME r) {
      left = l;
      right = r;
      up = left;
      down = right;
    }
    Register_Relative_Position(REGISTER_NAME l, REGISTER_NAME r,
                               REGISTER_NAME u, REGISTER_NAME d) {
      left = l;
      right = r;
      up = u;
      down = d;
    }
    Register_Relative_Position() {
      left = UNKNOWN;
      right = UNKNOWN;
      up = UNKNOWN;
      down = UNKNOWN;
    }
  };

  explicit RegistersViewX86(QWidget* parent);
  ~RegistersViewX86();

  QSize sizeHint() const;

  static void* operator new(size_t size);
  static void operator delete(void* p);
  int getEstimateHeight();

 public slots:
  virtual void refreshShortcutsSlot();
  virtual void displayCustomContextMenuSlot(QPoint pos);
  virtual void debugStateChangedSlot(DBGSTATE state);
  void reload();
  void ShowFPU(bool set_showfpu);
  void onChangeFPUViewAction();
  void SetChangeButton(QPushButton* push_button);

 signals:
  void refresh();

 protected:
  // events
  virtual void mousePressEvent(QMouseEvent* event);
  virtual void mouseDoubleClickEvent(QMouseEvent* event);
  virtual void mouseMoveEvent(QMouseEvent* event);
  virtual void paintEvent(QPaintEvent* event);
  virtual void keyPressEvent(QKeyEvent* event);

  // use-in-class-only methods
  void drawRegister(QPainter* p, REGISTER_NAME reg, char* value);
  char* registerValue(const REGDUMP* regd, const REGISTER_NAME reg);
  bool identifyRegister(const int y, const int x, REGISTER_NAME* clickedReg);
  QString helpRegister(REGISTER_NAME reg);

  void ensureRegisterVisible(REGISTER_NAME reg);

 protected slots:
  void InitMappings();
  void fontsUpdatedSlot();
  QString getRegisterLabel(REGISTER_NAME);
  int CompareRegisters(const REGISTER_NAME reg_name, REGDUMP* regdump1,
                       REGDUMP* regdump2);
  SIZE_T GetSizeRegister(const REGISTER_NAME reg_name);
  QString GetRegStringValueFromValue(REGISTER_NAME reg, const char* value);
  QString GetTagWordStateString(unsigned short);
  // unsigned int GetTagWordValueFromString(const char* string);
  QString GetControlWordPCStateString(unsigned short);
  // unsigned int GetControlWordPCValueFromString(const char* string);
  QString GetControlWordRCStateString(unsigned short);
  // unsigned int GetControlWordRCValueFromString(const char* string);
  QString GetMxCsrRCStateString(unsigned short);
  // unsigned int GetMxCsrRCValueFromString(const char* string);
  // unsigned int GetStatusWordTOPValueFromString(const char* string);
  QString GetStatusWordTOPStateString(unsigned short state);
  void setRegisters(REGDUMP* reg);
  void appendRegister(QString& text, REGISTER_NAME reg, const char* name64,
                      const char* name32);

  void onCopyAllAction();

 protected:
  bool isActive;
  QPushButton* mChangeViewButton;
  bool mShowFpu;
  int mVScrollOffset;
  int mRowsNeeded;
  int yTopSpacing;
  int mButtonHeight;
  QSet<REGISTER_NAME> mUINTDISPLAY;
  QSet<REGISTER_NAME> mUSHORTDISPLAY;
  QSet<REGISTER_NAME> mDWORDDISPLAY;
  QSet<REGISTER_NAME> mBOOLDISPLAY;
  QSet<REGISTER_NAME> mLABELDISPLAY;
  QSet<REGISTER_NAME> mONLYMODULEANDLABELDISPLAY;
  QSet<REGISTER_NAME> mUNDODISPLAY;
  QSet<REGISTER_NAME> mSETONEZEROTOGGLE;
  QSet<REGISTER_NAME> mMODIFYDISPLAY;
  QSet<REGISTER_NAME> mFIELDVALUE;
  QSet<REGISTER_NAME> mTAGWORD;
  QSet<REGISTER_NAME> mCANSTOREADDRESS;
  QSet<REGISTER_NAME> mSEGMENTREGISTER;
  QSet<REGISTER_NAME> mINCREMENTDECREMET;
  QSet<REGISTER_NAME> mFPUx87_80BITSDISPLAY;
  QSet<REGISTER_NAME> mFPU;
  // holds current selected register
  REGISTER_NAME mSelected;
  // general purposes register id s (cax, ..., r8, ....)
  QSet<REGISTER_NAME> mGPR;
  // all flags
  QSet<REGISTER_NAME> mFlags;
  // FPU x87, XMM and MMX registers
  QSet<REGISTER_NAME> mFPUx87;
  QSet<REGISTER_NAME> mFPUMMX;
  QSet<REGISTER_NAME> mFPUXMM;
  QSet<REGISTER_NAME> mFPUYMM;
  // contains all id's of registers if there occurs a change
  QSet<REGISTER_NAME> mRegisterUpdates;
  // registers that do not allow changes
  QSet<REGISTER_NAME> mNoChange;
  // maps from id to name
  QMap<REGISTER_NAME, QString> mRegisterMapping;
  // contains viewport positions
  QMap<REGISTER_NAME, Register_Position> mRegisterPlaces;
  // contains names of closest registers in view
  QMap<REGISTER_NAME, Register_Relative_Position> mRegisterRelativePlaces;
  // contains a dump of the current register values
  REGDUMPX64 wRegDumpStruct;
  REGDUMPX64 wCipRegDumpStruct;
  // font measures (TODO: create a class that calculates all thos values)
  unsigned int mRowHeight, mCharWidth;
  // SIMD registers display mode
  char mFpuMode;  // 0 = order by ST(X), 1 = order by x87rX, 2 = MMX registers
  dsint mCip;
  std::vector<std::pair<const char*, uint8_t>> mHighlightRegs;
};

#define _WIN64 1

#endif  // REGISTERSVIEWX86_H
